package engine;

import java.util.ArrayList;

import utils.MyException;

public abstract class Game implements GameRO {

	//
	private String name;
	private int gameRate = 30;
	private Long previousPeriod = null, lastUpdate = null;
	private float fps, fpsUpdateFactor = 0.9f;

	private ArrayList<Room> rooms;
	private Room currentRoom;

	public Game(String name) {
		init();
		setName(name);
	}

	private void init() {
		fps = gameRate;
		rooms = new ArrayList<Room>();
		initialize();
		if (rooms.isEmpty())
			currentRoom = rooms.get(0);
		else
			currentRoom = null;
	}

	protected abstract void initialize();

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String getName() {
		return name;
	}

	// Rooms

	public void addRoom(Room room, boolean isPersistent, boolean runsInBackground) {
		room.setPersistent(isPersistent);
		room.setRunsInBackground(runsInBackground);
		rooms.add(room);
	}

	public Room getCurrentRoom() {
		return currentRoom;
	}

	public ArrayList<String> getRooms() {
		ArrayList<String> out = new ArrayList<String>();
		for (Room room : rooms)
			out.add(room.getName());
		return out;
	}

	public Room goToRoom(String roomName) throws MyException {
		Room out = null;
		for (Room room : rooms)
			if (room.getName().equals(roomName))
				goToRoom(out = room);
			else
				closeRoom(room);
		if (out == null)
			throw new MyException("Room", MyException.NOT_FOUND, roomName);
		return out;
	}

	private void goToRoom(Room room) {
		currentRoom = room;
	}

	private void closeRoom(Room room) {
		if (!room.isPersistent())
			room.restart();
	}

	public Room removeRoom(String roomName) throws MyException {
		Room out = null;
		for (Room room : rooms)
			if (room.getName().equals(roomName)) {
				removeRoom(out = room);
				break;
			}
		if (out == null)
			throw new MyException("Room", MyException.NOT_FOUND, roomName);
		return out;
	}

	private void removeRoom(Room room) {
		rooms.remove(room);
	}

	// FLOW

	public synchronized void step() {
		for (Room r : rooms)
			if (r.isRunsInBackground() || r == currentRoom)
				r.step(getFPSFactor());
		updateFPS();
	}

	public int getGameRate() {
		return gameRate;
	}

	public void setGameRate(int gameRate) {
		this.gameRate = Math.max(1, gameRate);
	}

	public float getFPS() {
		return fps;
	}
	
	private float updateFPS() {
		if (lastUpdate == null) {
			lastUpdate = System.nanoTime();
			previousPeriod = (long) (1000 / getGameRate());
			return gameRate;
		} else {
			long currentTime = System.nanoTime();
			long currentPeriod = currentTime - lastUpdate;
			fps = (float) (((fpsUpdateFactor / (currentPeriod) + (1 - fpsUpdateFactor) / previousPeriod)) * 1E9);
			lastUpdate = currentTime;
			previousPeriod = currentPeriod;
			return fps;
		}
	}

	public float getFPSFactor() {
		return gameRate / fps;
	}
}
